package org.hzero.business.service.impl;

import io.choerodon.core.exception.CommonException;
import io.choerodon.core.oauth.DetailsHelper;
import io.choerodon.mybatis.common.BaseMapper;
import io.choerodon.mybatis.helper.OptionalHelper;
import org.activiti.rest.service.api.runtime.process.ProcessInstanceCreateRequest;
import org.activiti.rest.service.api.runtime.process.ProcessInstanceResponse;
import org.hzero.boot.platform.plugin.hr.EmployeeHelper;
import org.hzero.boot.workflow.WorkflowClient;
import org.hzero.business.KnifeCheckStatusUtil;
import org.hzero.business.KnifeWorkflowKeyUtil;
import org.hzero.business.KnifeWorkflowUtil;
import org.hzero.business.halm.constant.Constants;
import org.hzero.business.service.KnifeOrderHelper;
import org.hzero.mybatis.domian.Condition;
import org.hzero.mybatis.util.Sqls;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl;

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @ClassName: UpdateHeaderStatusInterfaceImpl
 * @Description: 单据通用方法
 * @author: tianhao.luo@hand-china.com
 * @date: 2020/10/2614:26
 */
public class KnifeOrderHelperImpl<Header, Line> implements KnifeOrderHelper<Header, Line> {

    @Autowired
    private WorkflowClient workflowClient;

    @Autowired
    private KnifeWorkflowUtil knifeWorkflowUtil;

    @Autowired
    private BaseMapper<Header> headerBaseMapper;
    @Autowired
    private BaseMapper<Line> lineBaseMapper;

    private static final Logger LOGGER = LoggerFactory.getLogger(KnifeOrderHelperImpl.class);


    /**
     * 单状态变更
     *
     * @param headerId        单头
     * @param statusFieldName 单据状态字段名
     * @param statusCode      状态
     * @return 单头
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Header updateOrderStatus(Long headerId, String statusFieldName, String statusCode) {
        Assert.isTrue(!StringUtils.isEmpty(statusFieldName), "单据状态字段名不能为空！");
        Assert.isTrue(!StringUtils.isEmpty(statusCode), "状态不能为空！");
        Assert.isTrue(!ObjectUtils.isEmpty(headerId), "单头id不能为空！");
        Header orderHeader = headerBaseMapper.selectByPrimaryKey(headerId);
        Assert.notNull(orderHeader, headerId + "的单据头不存在！");
        Class<?> aClass = orderHeader.getClass();
        try {
            // 加载出当前实体类的状态字段
            Field status = aClass.getDeclaredField(statusFieldName);
            // 确定是否能够执行
            if (!status.isAccessible()) {
                status.setAccessible(true);
            }
            // 更新状态
            status.set(orderHeader, statusCode);
            // 只更新单据的状态字段
            OptionalHelper.optional(Collections.singletonList(statusFieldName));
            headerBaseMapper.updateOptional(orderHeader);
        } catch (Exception e) {
            covertErrorMsg(e);
        }
        // 返回传入的id的单据
        return orderHeader;
    }

    /**
     * 撤回单据
     *
     * @param tClass          实体类型
     * @param headerId        单头id
     * @param statusFieldName 状态字段
     * @return 对像
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Header revokeOrder(Class<Header> tClass, Long headerId, String statusFieldName) {
        Assert.isTrue(!StringUtils.isEmpty(statusFieldName), "单据状态字段名不能为空！");
        Assert.isTrue(!ObjectUtils.isEmpty(headerId), "单头id不能为空！");
        // 校验单据状态
        Header order = headerBaseMapper.selectByPrimaryKey(headerId);
        try {
            // 获取状态属性字段
            Field dbStatusField = tClass.getDeclaredField(statusFieldName);
            // 是否能够执行
            if (!dbStatusField.isAccessible()) {
                dbStatusField.setAccessible(true);
            }
            Object statusObj = dbStatusField.get(order);
            Assert.notNull(statusObj, "单据的状态不能为空！");
            String dbStatus = statusObj.toString();
            // 先校验该单据是否为NEW状态，如果是NEW表示已经撤回了
            if (Constants.HalmOrderStatusCode.NEW.equals(dbStatus)) {
                throw new CommonException("该单据状态已为新建！");
            } else {
                KnifeCheckStatusUtil.checkOne(
                        order
                        , statusFieldName
                        , "非审批中状态，无法撤回"
                        , Constants.HalmOrderStatusCode.APPROVING);
                String businessKey = KnifeWorkflowKeyUtil.createBusinessKey(tClass, headerId);
                // 是否有审批记录了，有就不允许继续撤回
                List<Long> idList = knifeWorkflowUtil.listApprovedByBusinessKey(businessKey);
                Assert.isTrue(CollectionUtils.isEmpty(idList), "已被审批，无法撤回");
                try {
                    // 重置单据状态为NEW
                    dbStatusField.set(order, Constants.HalmOrderStatusCode.NEW);
                    // 只更新单据状态字段
                    OptionalHelper.optional(Collections.singletonList(statusFieldName));
                    headerBaseMapper.updateOptional(order);
                } catch (Exception e) {
                    covertErrorMsg(e);
                    throw new CommonException("撤回失败，请联系管理员");
                }
                workflowClient.procRevokeByBusinessKey(businessKey, DetailsHelper.getUserDetails().getTenantId());
            }
        } catch (Exception e) {
            covertErrorMsg(e);
            throw new CommonException("撤回失败，请联系管理员");
        }
        return order;
    }


    /**
     * 通用提交工作流
     *
     * @param paramMap         流程变量参数
     * @param processDefineKey 流程定义key
     * @param tClass           用来创建业务主键，提交单据的头id所在的类
     * @param headerId         单头id
     * @param statusFieldName  单据状态字段
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void submitOrder(Class<Header> tClass, Map<String, Object> paramMap, String processDefineKey, Long headerId, String statusFieldName) {
        Assert.isTrue(!StringUtils.isEmpty(processDefineKey), "流程定义key不能为空！");
        Assert.isTrue(!ObjectUtils.isEmpty(headerId), "单头id不能为空！");
        // 校验该单据对应的状态是否能够提交
        Header header = headerBaseMapper.selectByPrimaryKey(headerId);
        // 校验单据状态是否在NEW和REJECTED中
        Field statusField = KnifeCheckStatusUtil.checkOneAndGetStatusField(header,
                statusFieldName,
                "已经提交了该单据！",
                Constants.HalmOrderStatusCode.NEW,
                Constants.HalmOrderStatusCode.REJECTED);
        //更新单据的状态为审批中
        if (!statusField.isAccessible()) {
            statusField.setAccessible(true);
        }
        try {
            statusField.set(header, Constants.HalmOrderStatusCode.APPROVING);
        } catch (IllegalAccessException e) {
            covertErrorMsg(e);
        }
        // 只更新单据状态
        OptionalHelper.optional(Collections.singletonList(statusFieldName));
        headerBaseMapper.updateOptional(header);
        //构造
        String businessKey = KnifeWorkflowKeyUtil.createBusinessKey(tClass, headerId);
        //构造工作流请求参数
        ProcessInstanceCreateRequest request = knifeWorkflowUtil.getRequest(paramMap, processDefineKey, businessKey);
        //校验必填流程变量
        knifeWorkflowUtil.checkStartVar(request, processDefineKey);
        //提交工作流
        Long tenantId = DetailsHelper.getUserDetails().getTenantId();
        String employeeNum = EmployeeHelper.getEmployeeNum(DetailsHelper.getUserDetails().getUserId(), tenantId);
        ResponseEntity<ProcessInstanceResponse> entity = workflowClient.startUp(tenantId, employeeNum, request);
        //校验是否提交成功
        KnifeWorkflowUtil.checkStartUp(entity);
    }

    /**
     * 生成是否单据创建人登录的标记
     *
     * @param order              单据
     * @param createdByFieldName 创建人字段名
     * @return 是否登录的标记，登录返回1，否则为0
     */
    @Override
    public Long getLoginFlag(Header order, String createdByFieldName) {
        Assert.notNull(order, "单据不能为空！");
        Assert.isTrue(!StringUtils.isEmpty(createdByFieldName), "创建人字段名不能为空！");
        try {
            Class<?> aClass = order.getClass();
            Field createdBy = KnifeCheckStatusUtil.getExistField(aClass, createdByFieldName);
            createdBy.setAccessible(true);
            Object createdById = createdBy.get(order);
            Long loginUserId = DetailsHelper.getUserDetails().getUserId();
            if (createdById.toString().equals(String.valueOf(loginUserId))) {
                return 1L;
            } else {
                return 0L;
            }
        } catch (Exception e) {
            covertErrorMsg(e);
            throw new CommonException("生成loginFlag失败");
        }
    }

    @Override
    public Header deleteHeaderAndLine(Long headerId, String statusCodeFieldName, String linesHeaderIdColumn, String... statusArray) {
        Header header = headerBaseMapper.selectByPrimaryKey(headerId);
        Assert.isTrue(!ObjectUtils.isEmpty(header), "单据不存在！");
        KnifeCheckStatusUtil.checkOne(
                header
                , statusCodeFieldName
                , "单据不允许删除！"
                , statusArray);
        // 获取lineBaseMapper的泛型，主要是为了我们查询传入查询对象类型参数
        // ,lineBaseMapper.getClass()获取代理对象，getGenericInterfaces()获取代理对象继承或实现的类。
        // 将获取到的代理对象来获取它实现或继承的类
        Class genericInterface = (Class) lineBaseMapper.getClass().getGenericInterfaces()[0];
        ParameterizedTypeImpl baseMapperType = (ParameterizedTypeImpl) genericInterface.getGenericInterfaces()[0];
        //获取获取BaseMapper的泛型的实际类型
        Class lineClass = (Class) baseMapperType.getActualTypeArguments()[0];
        List<Line> lines = lineBaseMapper.selectByCondition(Condition.builder(lineClass)
                .andWhere(Sqls.custom().andEqualTo(linesHeaderIdColumn, headerId))
                .build());
        // 都需要删除头和行记录
        headerBaseMapper.delete(header);
        for (Line line : lines) {
            lineBaseMapper.delete(line);
        }
        return header;
    }

    /**
     * 构造异常信息提示
     *
     * @param e       异常
     * @param collect 堆栈信息
     * @return 异常信息
     */
    private String buildErrorMsgFromStack(Exception e, List<StackTraceElement> collect) {
        return e.getMessage() + "异常位置出现在" + this.getClass().getSimpleName() + "的第" + collect.get(0).getLineNumber() + "行";
    }

    /**
     * 转化异常消息
     *
     * @param e 异常
     */
    private void covertErrorMsg(Exception e) {
        // 转化报错信息
        StackTraceElement[] stackTrace = e.getStackTrace();
        List<StackTraceElement> collect = Arrays.stream(stackTrace)
                .filter(item -> item.getClassName().equals(this.getClass().getName()))
                .collect(Collectors.toList());
        String errorMsg;
        if (!CollectionUtils.isEmpty(collect)) {
            errorMsg = buildErrorMsgFromStack(e, collect);
        } else {
            errorMsg = e.getMessage() == null ? "空指针" : e.getMessage();
        }
        LOGGER.error(errorMsg);
    }
}
